﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Resources;
using System.Windows.Forms;

namespace Useful.GUI
{
    public class GuiHelper
    {
        public static bool? GetCheckBoxState(CheckState cs)
        {
            if (cs == CheckState.Checked)
                return true;
            else if (cs == CheckState.Unchecked)
                return false;
            else
                return null;
        }

        public static int? GetCheckBoxIntState(CheckState cs)
        {
            if (cs == CheckState.Checked)
                return 1;
            else if (cs == CheckState.Unchecked)
                return 0;
            else
                return null;
        }

        public static object GetCheckBoxDbState(CheckState cs)
        {
            if (cs == CheckState.Checked)
                return true;
            else if (cs == CheckState.Unchecked)
                return false;
            else
                return DBNull.Value;
        }

        public static void SetCheckBoxState(CheckBox cb, bool? check)
        {
            if (check == null)
                cb.CheckState = CheckState.Indeterminate;
            else if (check.Value)
                cb.CheckState = CheckState.Checked;
            else
                cb.CheckState = CheckState.Unchecked;
        }

        public static void SetCheckBoxState(CheckBox cb, object value)
        {
            if (value == DBNull.Value)
                cb.CheckState = CheckState.Indeterminate;
            else
                cb.CheckState = Convert.ToBoolean(value) ? CheckState.Checked : CheckState.Unchecked;
        }

        public static DateTime? GetDateTime(DateTimePicker dtp)
        {
            if (dtp.Checked)
                return dtp.Value;
            else
                return null;
        }

        public static object GetDbDateTime(DateTimePicker dtp)
        {
            if (dtp.Checked)
                return dtp.Value;
            else
                return DBNull.Value;
        }

        public static object GetDbDateTime(DateTime? dt)
        {
            if (dt != null)
                return dt.Value;
            else
                return DBNull.Value;
        }

        private static ResourceManager resourceMan = null;
        public static ResourceManager ResourceManager
        {
            get
            {
                if (resourceMan == null)
                    resourceMan = new ResourceManager("Useful.Properties.Resources", typeof(GuiHelper).Assembly);

                return resourceMan;
            }
        }

        private static DataGridViewColumn DataGridViewAddColumn<T>(DataGridView grid, string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode sizeMode, string toolTipText, string format) where T : DataGridViewColumn, new()
        {
            DataGridViewColumn res = new T();

            DataGridViewAddColumn(grid, res, name, header, dataPropertyName, sizeMode, toolTipText, format);

            return res;
        }

        private static DataGridViewColumn DataGridViewAddColumn<T>(DataGridView grid, string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode sizeMode, string format) where T : DataGridViewColumn, new()
        {
            return DataGridViewAddColumn<T>(grid, name, header, dataPropertyName, sizeMode, null, format);
        }

        public static void DataGridViewAddColumn(DataGridView grid, DataGridViewColumn column, string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode? sizeMode, string toolTipText, string format)
        {
            column.Name = name;
            column.HeaderText = header;
            column.DataPropertyName = dataPropertyName;
            column.SortMode = DataGridViewColumnSortMode.NotSortable;

            if (sizeMode != null)
                column.AutoSizeMode = sizeMode.Value;

            if (toolTipText != null)
                column.HeaderCell.ToolTipText = toolTipText;

            if (format != null)
                column.DefaultCellStyle.Format = format;

            if (grid != null)
                grid.Columns.Add(column);
        }

        public static DataGridViewColumn DataGridViewAddTextColumn(DataGridView grid, string name, string header, string dataPropertyName, DataGridViewAutoSizeColumnMode sizeMode, string format)
        {
            return DataGridViewAddColumn<DataGridViewTextBoxColumn>(grid, name, header, dataPropertyName, sizeMode, format);
        }

        public static DataGridViewColumn DataGridViewAddTextColumn(DataGridView grid, string dataPropertyName, string header)
        {
            return DataGridViewAddColumn<DataGridViewTextBoxColumn>(grid, dataPropertyName, header, dataPropertyName, DataGridViewAutoSizeColumnMode.NotSet, null);
        }

        public static DataGridViewColumn DataGridViewAddTextColumn(DataGridView grid, string dataPropertyName, string header, DataGridViewAutoSizeColumnMode sizeMode)
        {
            return DataGridViewAddColumn<DataGridViewTextBoxColumn>(grid, dataPropertyName, header, dataPropertyName, sizeMode, null);
        }

        public static DataGridViewColumn DataGridViewAddBoolColumn(DataGridView grid, string name, string header, string dataPropertyName)
        {
            return DataGridViewAddColumn<DataGridViewCheckBoxColumn>(grid, name, header, dataPropertyName, DataGridViewAutoSizeColumnMode.AllCells, null);
        }

        public static DataGridViewColumn DataGridViewSetColumnPropertiesText(DataGridView grid, string name, string header, string prop, bool visible, bool _readonly, int displayIndex, DataGridViewAutoSizeColumnMode autoSizeMode, DataGridViewColumnSortMode sortMode)
        {
            return DataGridViewSetColumnProperties<DataGridViewTextBoxColumn>(grid, name, header, prop, visible, _readonly, displayIndex, autoSizeMode, sortMode);
        }

        public static DataGridViewColumn DataGridViewSetColumnPropertiesBool(DataGridView grid, string name, string header, string prop, bool visible, bool _readonly, int displayIndex, DataGridViewAutoSizeColumnMode autoSizeMode, DataGridViewColumnSortMode sortMode, bool threeState)
        {
            DataGridViewCheckBoxColumn c = (DataGridViewCheckBoxColumn)DataGridViewSetColumnProperties<DataGridViewCheckBoxColumn>(grid, name, header, prop, visible, _readonly, displayIndex, autoSizeMode, sortMode);

            c.ThreeState = threeState;

            return c;
        }

        public static DataGridViewColumn DataGridViewSetColumnPropertiesBool(DataGridView grid, string name, string header, string prop, bool visible, bool _readonly, int displayIndex, DataGridViewAutoSizeColumnMode autoSizeMode, DataGridViewColumnSortMode sortMode)
        {
            return DataGridViewSetColumnProperties<DataGridViewCheckBoxColumn>(grid, name, header, prop, visible, _readonly, displayIndex, autoSizeMode, sortMode);
        }

        private static DataGridViewColumn DataGridViewSetColumnProperties<T>(DataGridView grid, string name, string header, string prop, bool visible, bool _readonly, int displayIndex, DataGridViewAutoSizeColumnMode autoSizeMode, DataGridViewColumnSortMode sortMode) where T : DataGridViewColumn, new()
        {
            DataGridViewColumn res = null;

            if (grid.Columns.Contains(name))
            {
                res = grid.Columns[name];
            }
            else
            {
                res = new T();
                res.Name = name;
                grid.Columns.Add(res);
            }

            res.DataPropertyName = prop;
            res.Visible = visible;
            res.ReadOnly = _readonly;

            if (displayIndex < grid.ColumnCount)
                res.DisplayIndex = displayIndex;

            res.HeaderText = header;
            res.AutoSizeMode = autoSizeMode;
            res.SortMode = sortMode;

            return res;
        }

        public static void DataGridViewHideColumn(DataGridView grid, string name)
        {
            if (grid.Columns.Contains(name))
                grid.Columns[name].Visible = false;
        }

        private static int PropCompareCore<T>(T x, T y, string prop)
        {
            int res = 0;

            Type t = typeof(T);
            System.Reflection.PropertyInfo pi = t.GetProperty(prop);
            object vX = pi.GetValue(x, null);
            object vY = pi.GetValue(y, null);

            if (vX == null && vY == null)
                res = 0;
            else if (vX == null && vY != null)
                res = -1;
            else if (vX != null && vY == null)
                res = 1;
            else if (vX is IComparable)
                res = ((IComparable)vX).CompareTo(vY);
            else
                throw new Exception(Useful.Localization.Localizator.GetString("Свойство не поддерживает сравнение"));

            return res;
        }

        private static int PropCompare<T>(T x, T y, IEnumerable<string> props)
        {
            int res = 0;

            foreach (string prop in props)
            {
                res = PropCompareCore<T>(x, y, prop);
                if (res != 0)
                    break;
            }

            return res;
        }

        public static void SortList<T>(DataGridView grid, int ColumnIndex, List<T> list, Dictionary<string, string[]> props, SortOrder sortOrder)
        {
            if (grid == null)
                throw new ArgumentNullException("grid в SortList<T>");

            if (!(ColumnIndex >= 0 && ColumnIndex < grid.Columns.Count))
                throw new ArgumentOutOfRangeException("ColumnIndex = " + ColumnIndex + " в SortList<T>");

            if (list == null)
                throw new ArgumentNullException("list в SortList<T>");

            if (props == null)
                throw new ArgumentNullException("props в SortList<T>");

            //if (grid != null && list != null && props != null && ColumnIndex >= 0 && !(ColumnIndex < grid.Columns.Count))
            {
                grid.EndEdit();

                string prop = grid.Columns[ColumnIndex].Name;

                if (!props.ContainsKey(prop))
                    throw new Exception("В словаре props отсутствует ключ '" + prop + "' в SortList<T>");

                foreach (DataGridViewColumn c in grid.Columns)
                    c.HeaderCell.SortGlyphDirection = SortOrder.None;

                grid.Columns[ColumnIndex].HeaderCell.SortGlyphDirection = sortOrder;

                bool asc = grid.Columns[ColumnIndex].HeaderCell.SortGlyphDirection == SortOrder.Ascending;

                try
                {
                    list.Sort((x, y) =>
                    {
                        int res = PropCompare<T>(x, y, props[prop]);

                        if (!asc)
                        {
                            if (res != 0)
                                res = res == 1 ? -1 : 1;
                        }

                        return res;
                    });
                }
                catch (Exception ex)
                {
                    throw new Exception("Ошибка при сортировке в SortList<T>: " + ex.Message, ex);
                }

                grid.Refresh();
            }
            //else
            //    throw new ArgumentNullException("Не допустимые аргументы в GuiHelper.SortList<T>");
        }

        public static SortOrder SortList<T>(DataGridView grid, int ColumnIndex, List<T> list, Dictionary<string, string[]> props)
        {
            if (grid == null)
                throw new ArgumentNullException("grid");

            if (!(ColumnIndex >= 0 && ColumnIndex < grid.Columns.Count))
                throw new ArgumentOutOfRangeException("ColumnIndex = " + ColumnIndex);

            if (list == null)
                throw new ArgumentNullException("list");

            if (props == null)
                throw new ArgumentNullException("props");


            SortOrder needSortOrder;

            if (grid.Columns[ColumnIndex].HeaderCell.SortGlyphDirection == SortOrder.Ascending)
                needSortOrder = SortOrder.Descending;
            else
                needSortOrder = SortOrder.Ascending;

            SortList<T>(grid, ColumnIndex, list, props, needSortOrder);

            return needSortOrder;
        }

        private static void SimpleInitComboBox(ComboBox cb, string display, string value, object bs)
        {
            try
            {
                cb.DisplayMember = display;
                cb.ValueMember = value;
                cb.DataSource = bs;
                cb.DropDownStyle = ComboBoxStyle.DropDownList;
            }
            catch (Exception ex)
            {
                throw new GuiException("Инициализация ComboBox", ex);
            }
        }

        public static void InitComboBox(ComboBox cb, string display, string value, BindingSource bs, object dataSource)
        {
            bs.DataSource = dataSource;

            SimpleInitComboBox(cb, display, value, bs);
        }

        public static ComboBox InitComboBox(ComboBox cb, string display, string value, object dataSource)
        {
            if (dataSource is BindingSource)
                throw new ArgumentException("dataSource не может быть BindingSource");

            BindingSource bs;

            if (cb.DataSource != null && cb.DataSource is BindingSource)
                bs = (BindingSource)cb.DataSource;
            else
                bs = new BindingSource();

            InitComboBox(cb, display, value, bs, dataSource);

            return cb;
        }

        public static ComboBox InitComboBox2(ComboBox cb, string display, string value, BindingSource bs)
        {
            SimpleInitComboBox(cb, display, value, bs);

            return cb;
        }


        public static int? GetComboBoxValue(ComboBox cb)
        {
            if (cb.SelectedValue != null)
                return ((int)cb.SelectedValue) != -1 ? (int?)cb.SelectedValue : null;
            else
                return null;
        }

        public static int? GetComboBoxValue2(ComboBox cb)
        {
            if (cb.SelectedValue != null && cb.SelectedValue != DBNull.Value)
                return ((int)cb.SelectedValue) != -1 ? (int?)cb.SelectedValue : null;
            else
                return null;
        }

        public static void SetComboBoxValue(ComboBox cb, object val)
        {
            if (val != null && val != DBNull.Value)
                cb.SelectedValue = val;
            else
                cb.SelectedValue = -1;
        }

        public static void SetComboBoxValue2(ComboBox cb, object val)
        {
            if (val != null && val != DBNull.Value)
                cb.SelectedValue = val;
            else
                cb.SelectedValue = DBNull.Value;
        }

        public static int GetComboBoxMaxTextWithCore(ComboBox cmbValue)
        {
            int maxSize = -1;

            Graphics g = Graphics.FromHwnd(cmbValue.Handle);

            foreach (object item in cmbValue.Items)
            {
                string str = cmbValue.GetItemText(item);

                //SizeF size = TextRenderer.MeasureText(str, cbList.Font);
                SizeF size = g.MeasureString(str, cmbValue.Font); // Так оказалость точнее

                if (size.Width > maxSize)
                    maxSize = (int)size.Width;
            }

            return maxSize;
        }

        public static int GetComboBoxMaxTextWith(ComboBox cmbValue)
        {
            return GetComboBoxMaxTextWithCore(cmbValue) + SystemInformation.VerticalScrollBarArrowHeight + 5;
        }

        public static int GetListBoxMaxTextWithCore(ListBox lbValue)
        {
            int maxSize = -1;

            Graphics g = Graphics.FromHwnd(lbValue.Handle);

            foreach (object item in lbValue.Items)
            {
                string str = lbValue.GetItemText(item);

                //SizeF size = TextRenderer.MeasureText(str, cbList.Font);
                SizeF size = g.MeasureString(str, lbValue.Font); // Так оказалость точнее

                if (size.Width > maxSize)
                    maxSize = (int)size.Width;
            }

            return maxSize;
        }

        public static int GetListBoxMaxTextWith(ListBox lbValue)
        {
            return GetListBoxMaxTextWithCore(lbValue) + SystemInformation.VerticalScrollBarArrowHeight + 5;
        }

        public delegate void ProcessActionDelegate();
        public delegate void ProcessExceptionDelegate(string msg, Exception ex);
        public delegate void ProcessActionDelegate2(object context);

        public static void ProcessAction(string actionName, ProcessActionDelegate action, ProcessExceptionDelegate exception)
        {
            try
            {
                action();
            }
            catch (Exception ex)
            {
                exception(actionName, ex);
            }
        }

        public static DialogResult GetFolderName(string header, ref string folderName)
        {
            FolderBrowserDialog form = new FolderBrowserDialog();
            form.Description = header;
            form.SelectedPath = folderName;
            DialogResult res = form.ShowDialog();

            if (res == DialogResult.OK && form.SelectedPath != String.Empty)
            {
                folderName = form.SelectedPath;
            }
            else
                folderName = null;

            return res;
        }

        public delegate string GetComboBoxDrawItemParamsDelegate(object item, ref Color foreColor, ref Color backColor);

        public static void ComboBoxDrawItem(string actionName, object sender, DrawItemEventArgs e, GetComboBoxDrawItemParamsDelegate GetItemParams, ProcessExceptionDelegate exception)
        {
            ProcessAction(actionName, () =>
            {
                ComboBox cb = (ComboBox)sender;

                Color foreColor = e.ForeColor;
                Color backColor = e.BackColor;

                string text = GetItemParams(cb.Items[e.Index], ref foreColor, ref backColor);

                TextRenderer.DrawText(e.Graphics, text, e.Font, e.Bounds, foreColor, backColor, TextFormatFlags.Default);
            },
            exception
            );
        }

        public delegate bool GetComboBoxDrawItemSpecDelegate(object item, out string text);

        public static void ComboBoxDrawItem(string actionName, Color color, Color selectedColor, object sender, DrawItemEventArgs e, GetComboBoxDrawItemSpecDelegate GetItemParams, ProcessExceptionDelegate exception)
        {
            ProcessAction(actionName, () =>
            {
                if (e.Index == -1)
                    return;

                ComboBox cb = (ComboBox)sender;

                Color foreColor = e.ForeColor;
                Color backColor = e.BackColor;

                string text;

                if (GetItemParams(cb.Items[e.Index], out text))
                {
                    if ((e.State & DrawItemState.Selected) != DrawItemState.Selected)
                    {
                        foreColor = color;
                    }
                    else
                    {
                        foreColor = selectedColor;
                        backColor = color;
                    }
                }

                TextRenderer.DrawText(e.Graphics, text, e.Font, e.Bounds, foreColor, backColor, TextFormatFlags.Default);
            },
            exception
            );
        }

        public static Bitmap GetScreenShot()
        {
            return GDI.ScreenShot(Screen.PrimaryScreen.Bounds);
        }

        public static string SaveScreenShot(string filename)
        {
            using (Bitmap bmp = GetScreenShot())
            {
                try
                {
                    bmp.Save(filename);
                }
                catch (Exception ex)
                {
                    throw new SystemGuiException(String.Format("Ошибка сохранения скриншота в файл '{0}'", filename), ex);
                }
            }
            return filename;
        }

        public static string AppendFilter(string old, string append)
        {
            if (String.IsNullOrEmpty(old) && String.IsNullOrEmpty(append))
                return String.Empty;
            else if (!String.IsNullOrEmpty(old) && String.IsNullOrEmpty(append))
                return old;
            else if (String.IsNullOrEmpty(old) && !String.IsNullOrEmpty(append))
                return append;
            else
                return "(" + old + ") AND (" + append + ")";
        }

        public static string FormatExceptionMsg(string formName, string msg, Exception ex)
        {
            return String.Format("'{0}': {1}:\r\n{2}", formName, msg, ex.Message);
        }

		public static ListBox InitListBox(ListBox lb, string display, string value, object dataSource)
		{
			if (dataSource is BindingSource)
				throw new ArgumentException("dataSource не может быть BindingSource");

			BindingSource bs;

			if (lb.DataSource != null && lb.DataSource is BindingSource)
				bs = (BindingSource)lb.DataSource;
			else
				bs = new BindingSource();

			bs.DataSource = dataSource;

			lb.DisplayMember = display;
			lb.ValueMember = value;
			lb.DataSource = bs;

			return lb;
		}
	}
}